
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;

/**
 * A class representing a Driver. A Driver can work
 * for a limited number of hours a day and a week, and can work
 * no longer than MAX_HOURS_WORKED_IN_ROW straight.
 *
 * A driver also contains a unique ID, used for database
 * access.
 * @author cristina
 * @version 1.1
 */
public class Driver implements Comparable<Driver>
{

    //The maximum hours a driver can work for without a break
    private static final int MAX_MINUTES_WORKED_IN_ROW = 300;
    //Maximum hours a driver can work in a day
    private static final int MAX_MINUTES_PER_DAY = 600;
    //Maximum hours a driver can work in a week
    private static final int MAX_MINUTES_PER_WEEK = 3000;
    //Duration of break in minutes
    private static final int BREAK_DURATION_MINUTES = 60;
    // Maximum number of shifts per day
    private static final int MAX_SHIFTS = 2;
    //Minutes per day
    private static final int MINUTES_PER_DAY = 1440;
    //The weighting that the minutes driven today has
    private static final int MINUTES_DAY_WEIGHT = 10;
    //The weighting the minutes driven this week has
    private static final int MINUTES_WEEK_WEIGHT = 4;
    //The weighting the minutes driven this year has
    private static final int MINUTES_YEAR_WEIGHT = 2;

    //Information about all drivers
    public static DriverMinutesWorked driverInfo;
    
    //A unique ID of a driver, used for database access.
    private int driverID;
    //The minutes worked by a driver in a row
    private int minutesWorkedInRow;
    //The minutes this driver has worked today
    private int minutesWorkedToday;
    //The shifts the driver has worked today
    private ArrayList<DriverShift> shifts;
    //Whether the driver has already had a break
    private boolean hasHadBreak;

   


    static {
        database.openBusDatabase();
        /*System.out.println("In static");
        driverInfo = new HashMap<Integer, DriverMinutesWorked>();
        ArrayList<Driver> drivers = getDrivers();
        for (int i = 0; i < drivers.size(); i++)
        {
            System.out.println("Placing driver: " + drivers.get(i) + "into hashmap");
            driverInfo.put(drivers.get(i).getDriverID(), new DriverMinutesWorked(drivers.get(i)));
            System.out.println();
        }*/
        //database.busDatabase.close();

        driverInfo = new DriverMinutesWorked();

        //System.out.println("Getting driver 2064: " + driverInfo.get(drivers.get(0)));
    }
    
    /**
     * Constructs a driver object. A driver has an ID.
     * @param driverID The ID of a driver
     */
    public Driver(int driverID)
    {
        this.driverID = driverID;
        this.minutesWorkedToday = 0;
        this.shifts = new ArrayList<DriverShift>();
        this.hasHadBreak = false;

        //this.driverInfo = new DriverMinutesWorked(this);
        //driverInfo.put(this.driverID, new DriverMinutesWorked(this));
    }

    /**
     * Check if a driver has worked too many hours
     * without a break.
     * @return true if they have worked too long, false otherwise.
     */
    public boolean hasWorkedTooLongInRow(int duration)
    {
        return (((this.minutesWorkedInRow + duration)) > MAX_MINUTES_WORKED_IN_ROW);
    }

    /**
     *
     * @return The id of this driver
     */
    public int getDriverID(){
        return this.driverID;
    }

    /**
     *
     * @return The hours worked in week by this driver
     */
    public int getMinutesWorkedInWeek(){

        return driverInfo.getMinutesWeek(this);
    }
    
    public int getMinutesWorkedInYear(){
        return driverInfo.getMinutesYear(this);
    }

    public String getName() {
        return DriverInfo.getName(this.driverID);
    }

    public String getNumber() {
        return DriverInfo.getNumber(this.driverID);
    }

    /**
     * Add shift to driver's timetable.
     * A shift can only be added when a driver has not worked too
     * long in a row, or worked too many minutes this week/year.
     * @param shift A shift to add
     * @return true, if shift added, false otherwise.
     */
    public boolean addShift(DriverShift shift)
    {
        //Service service = shift.getService();
        //int duration = service.getDurationInMinutes();

        //TESTING ONLY


        //System.out.println("Shift minutes: " + shift.getEndTime().get(Calendar.MINUTE));

        int duration = -1;
        int startMinutes = shift.getStartTime().get(Calendar.MINUTE) + (shift.getStartTime().get(Calendar.HOUR_OF_DAY)* 60);
        int endMinutes = shift.getEndTime().get(Calendar.MINUTE) + (shift.getEndTime().get(Calendar.HOUR_OF_DAY)* 60);

        if (startMinutes >= endMinutes)
            duration = MINUTES_PER_DAY - startMinutes
                                            + endMinutes;
        else
            duration = endMinutes - startMinutes;



        if ((((this.minutesWorkedToday + duration)) > MAX_MINUTES_PER_DAY)
                || ((getMinutesWorkedInWeek() + duration) > MAX_MINUTES_PER_WEEK))
        {
          //  System.out.println("Couldn't assign in first if");
            //System.out.println("Hours worked today: " + minutesWorkedToday / 60 + "Hours worked this week: " + getMinutesWorkedInWeek() / 60 + " and duration: " + duration / 60);
            return false;
        }


        if (duration <= 0)
        {
            System.out.println("Duration is: " + duration);
            System.out.println("Start: " + Service.calendarToString(shift.getStartTime()) + "\nEnd: " + Service.calendarToString(shift.getEndTime()));
        }
        //Check if already working during this time
        if (!this.shifts.isEmpty())
        {
            Iterator<DriverShift> driverShiftsIter = shifts.iterator();

            while (driverShiftsIter.hasNext())
            {
                if (driverShiftsIter.next().isShiftIn(shift))
                {
                   // System.out.println("Couldn't assign in second if in while");
                    return false; //Can't work, already doing shift at this time.
                }
            }
        }

        //System.out.println("Past if");
        
        //ADD A BREAK
        //if (this.driverID == 2071)
         //System.out.println("next duration: " + (service.getDurationInMinutes() + this.minutesWorkedInRow));
        if (((duration + this.minutesWorkedInRow)) >= MAX_MINUTES_WORKED_IN_ROW)
        {
            System.out.println("Attempting to add break for: " + this.driverID);
            if (hasHadBreak)
            {
                //System.out.println("Couldn't assign in has had break if");
                return false; //Can't work again today
            }
               
            //Let them have a break
            Calendar endBreak = (Calendar) shift.getStartTime().clone();
            endBreak.add(Calendar.MINUTE, BREAK_DURATION_MINUTES);
            DriverShift breakShift = new DriverShift(shift.getStartTime(), endBreak, null, null);
            this.minutesWorkedInRow = 0;
            this.hasHadBreak = true;

            System.out.println("Couldn't assign, assigning break");
            return false; //Can't work due to having break
        }
        
        this.minutesWorkedInRow += duration;
        this.minutesWorkedToday += duration;


        Integer hash = new Integer(this.driverID);
       // System.out.println("For driver: " + this.driverID + " with hash code: " + hash.hashCode() + "Adding " + shift);
        //System.out.println("Before adding: " + driverInfo.getMinutesWeek(this));
        driverInfo.addMinutes(this, duration);
        //System.out.println("After adding: " + driverInfo.getMinutesWeek(this));
        
        //DriverInfo.setHoursThisWeek(this.driverID, (getMinutesWorkedInWeek() + service.getDurationInMinutes()) / 60);
        //DriverInfo.setHoursThisYear(this.driverID, (getMinutesWorkedInWeek() + service.getDurationInMinutes()) / 60);
        
        this.shifts.add(shift);

        //System.out.println("Returning true.");
        return true;
    }
    
    /**
     * Compares a driver to another driver.
     * Comparing a driver takes into accound various information about
     * the driver in this order: minutes worked today/this week/this year.
     * @param other The other driver to compare
     * @return -1 if the other if larger than this, 1 if this is larger than other
     *          and 0 if they are equal.
     */
   @Override
   public int compareTo(Driver other)
   {
       if (((other.minutesWorkedToday * MINUTES_DAY_WEIGHT) 
               + (other.getMinutesWorkedInWeek() * MINUTES_WEEK_WEIGHT)
               + (other.getMinutesWorkedInYear() * MINUTES_YEAR_WEIGHT))
               < ((this.minutesWorkedToday * MINUTES_DAY_WEIGHT) 
               + (this.getMinutesWorkedInWeek() * MINUTES_WEEK_WEIGHT)
               + (other.getMinutesWorkedInYear() * MINUTES_YEAR_WEIGHT)))
           return 1;
       else if (((other.minutesWorkedToday * MINUTES_DAY_WEIGHT) 
               + (other.getMinutesWorkedInWeek() * MINUTES_WEEK_WEIGHT)
               + (other.getMinutesWorkedInYear() * MINUTES_YEAR_WEIGHT))
               > ((this.minutesWorkedToday * MINUTES_DAY_WEIGHT) 
               + (this.getMinutesWorkedInWeek() * MINUTES_WEEK_WEIGHT)
               + (other.getMinutesWorkedInYear() * MINUTES_YEAR_WEIGHT)))
           return -1;
       else
           return 0;
   }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Driver)
            return (this.compareTo((Driver) obj) == 0);
        else
            return super.equals(obj);
    }



    @Override
    public String toString() {
        return "Driver{" + "driverID=" + driverID + "minutesWorkedInRow=" + minutesWorkedInRow + "minutesWorkedToday=" + minutesWorkedToday + "hasHadBreak=" + hasHadBreak + '}';
    }

    @Override
    public int hashCode() {
        return this.driverID;
    }




   


   /**
    * Gets all drivers within the database
    * @return An ArrayList representing all drivers in the database
    */
    public static ArrayList<Driver> getDrivers(){
        int[] driverIDs = DriverInfo.getDrivers();
        ArrayList<Driver> drivers = new ArrayList<Driver>();

        for (int i = 0; i < driverIDs.length; i++)
        {
            drivers.add(new Driver(driverIDs[i]));
        }

        return drivers;
    }

    /**
     * Get all drivers available today
     * @return An ArrayList representing all drivers available today.
     */
    public static ArrayList<Driver> getAvailableDriversToday()
    {
        int[] driverIDs = DriverInfo.getDrivers();
        ArrayList<Driver> drivers = new ArrayList<Driver>();

        for (int i = 0; i < driverIDs.length; i++)
        {
            if (DriverInfo.isAvailable(driverIDs[i]))
                drivers.add(new Driver(driverIDs[i]));
        }

        return drivers;
    }

    public static ArrayList<Driver> getAvailableDriversOn(Date date)
    {
        int[] driverIDs = DriverInfo.getDrivers();
        ArrayList<Driver> drivers = new ArrayList<Driver>();

        for (int i = 0; i < driverIDs.length; i++)
        {
            if (DriverInfo.isAvailable(driverIDs[i], date))
                drivers.add(new Driver(driverIDs[i]));
        }

        return drivers;
    }

    public static void main(String[] args)
    {
        database.openBusDatabase();
       /* System.out.println("In main!");
        ArrayList<Driver> drivers = getDrivers();

        Calendar calTestStart = Calendar.getInstance();
        Calendar calTestEnd = (Calendar) calTestStart.clone();*/

        /*Roster r = new Roster();
        ArrayList<ArrayList<Bus>> busShiftsTest = r.assignBuses();
        ArrayList<DriverShift> driverShiftsTest = new ArrayList<DriverShift>();
        for (int i = 0; i < 5; i++)
            driverShiftsTest.add(new DriverShift(busShiftsTest.get(0).get(i).))*/

        Calendar start = Calendar.getInstance();
        Calendar end = (Calendar)start.clone();
        end.add(Calendar.MINUTE, 250);
        Driver testDriver = new Driver(2064);
        DriverShift test = new DriverShift(start, end, null, testDriver);

        System.out.println(testDriver);
        testDriver.addShift(test);
        System.out.println(testDriver);

        Calendar start2 = Calendar.getInstance();
        Calendar end2 = Calendar.getInstance();
        start2.add(Calendar.MINUTE, 300);
        end2.add(Calendar.MINUTE, 350);
        DriverShift test2 = new DriverShift(start2, end2, null, testDriver);
        testDriver.addShift(test2);
        System.out.println(testDriver);

        Calendar start3 = Calendar.getInstance();
        Calendar end3 = Calendar.getInstance();

        start3.add(Calendar.MINUTE, 350);
        end3.add(Calendar.MINUTE, 400);
        DriverShift test3 = new DriverShift(start3, end3, null, testDriver);
        testDriver.addShift(test3);

        

        /*Driver driverTest = new Driver(2064);
        Bus busTest = new Bus(574);
        BusShift firstShift = new BusShift();
        ArrayList<Service> servTest = Service.getAllServices(65, calTestEnd, TimetableInfo.timetableKind(calTestStart.getTime()));
        ArratList<DriverShift> shiftsTest = new ArrayList<DriverShift>();
        for (int i = 0; i < 6; i ++)
            shiftsTest.add(new DriverShift(servTest.get(i).getStartTime()), servTest.get(i).getEndTime(), new BusShift())
        DriverShift shiftTest = new DriverShift();*/

        /*for (int i = 0; i < driverInfo.size(); i++)
        {
            System.out.println("DriverID: " + drivers.get(i).hashCode());
            System.out.println("Driver: " + driverInfo.get(drivers.get(i).getDriverID()));
        }*/

        //System.out.println("Testing driver index 0.");
    }
}
